/*
 * @(#)MethodReflect.java 1.0 2008/12/13
 */
package stardust.core.java.reflect;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


/**
 * <code>MethodReflect</code> 类提供反射方法的操作，并支持除数组外的自动类型转换。
 * <p/>
 * @see ReflectUtil
 * @see TypeUtils
 * @see ReflectException
 * @version 1.0, 2008/12/19
 * @author ChenChang
 */
public class MethodReflect<T>
{

  /*
   * 在 Bean 模式时的反射源。
   */
  private T object;

  /**
   * 获取反射的源对象引用。
   * <p/>
   * @param object
   * 反射所作用的源对象。
   */
  public void setObject(T object)
  {
    if (object == null)
    {
      throw null;
    }

    this.object = object;
  }

  /**
   * 获取反射的源对象引用。
   * <p/>
   * @return 反射所作用的源对象。
   */
  public T getObject()
  {
    return this.object;
  }

  /**
   * 获取一个 Bean 模式的反射器，可以简化对象属性的 Get/Set 操作。
   */
  public MethodReflect()
  {
    this(null);
  }

  /**
   * 获取一个 Bean 模式的反射器，可以简化对象属性的 Get/Set 操作。
   * <p/>
   * @param object
   * 一个反射源对象，如果为 <tt>null</tt> 值会引发 {@link NullPointerException} 异常。
   */
  public MethodReflect(T object)
  {
    this.object = object;
  }

  /**
   * 调用对象属性的 Set 方法，只支持帕斯卡命名规范的名称。
   * <p/>
   * @param propertyName
   * 属性名称是一个 {@link String} 字符串。
   * @param value
   * 要设置到属性的值，如果不匹配会尝试自动转换，转换失败会抛出 {@link ReflectException} 异常。
   * <p/>
   * @throws ReflectException
   * 方法调用失败抛出此异常。
   * @throws ClassCastException
   * 属性值自动转换失败会抛出此异常。
   */
  public void set(String propertyName, Object value) throws ReflectException, ClassCastException
  {
    this.invoke(ReflectUtil.toSetter(ReflectUtil.trimSetterOrGetter(propertyName)), value);
  }

  /**
   * 调用对象属性的 Get 方法，只支持帕斯卡命名规范的名称。
   * <p/>
   * @param propertyName
   * 属性名称是一个 {@link String} 字符串。
   * <p/>
   * @return 调用属性 Get 方法的返回值。
   * <p/>
   * @throws ReflectException
   * 方法调用失败抛出此异常。
   * @throws ClassCastException
   * 属性值自动转换失败会抛出此异常。
   */
  public Object get(String propertyName) throws ReflectException, ClassCastException
  {
    return this.invoke(ReflectUtil.toGetter(ReflectUtil.trimSetterOrGetter(propertyName)));
  }

  /**
   * 调用对象的方法。
   * <p/>
   * @param methodName
   * 方法名称是一个 {@link String} 字符串。
   * @param args
   * 方法参数是可选的参数 {@link Object} 对象列表。
   * <p/>
   * @return 调用属性 Get 方法的返回值。
   * <p/>
   * @throws ReflectException
   * 方法调用失败抛出此异常。
   * @throws ClassCastException
   * 属性值自动转换失败会抛出此异常。
   */
  public Object invoke(String methodName, Object... args) throws ReflectException, ClassCastException
  {
    try
    {
      return MethodReflect.invoke(this.object, methodName, args);
    }
    catch(ClassCastException e)
    {
      throw new ReflectException(e);
    }
    catch(IllegalAccessException e)
    {
      throw new ReflectException(e);
    }
    catch(IllegalArgumentException e)
    {
      throw new ReflectException(e);
    }
    catch(InvocationTargetException e)
    {
      throw new ReflectException(e);
    }
  }

  /**
   * 通过方法名称执行反射操作，如果不能找到完全匹配的方法会尝试自动转换类型查找最匹配的方法，自动转换失败会抛出
   * {@link ClassCastException} 异常。
   * <p/>
   * @param object
   * 需要执行反射操作的对象。
   * @param methodName
   * 需要调用的方法名称。
   * @param args
   * 调用方法时附带的参数，它可以是一个数组。
   * <p/>
   * @return 方法执行所返回的结果。
   * <p/>
   * @th java.lang.ClassCastException
   * 如果是自动匹配的方法在无法自动转换参数时会引发此异常。
   * @th java.lang.IllegalAccessException
   * 由 {@link Method#invoke(Object, Object...)} 方法引发。
   * @th java.lang.IllegalArgumentException
   * 由 {@link Method#invoke(Object, Object...)} 方法引发。
   * @th java.lang.reflect.InvocationTargetException
   * 由 {@link Method#invoke(Object, Object...)} 方法引发。
   */
  public static Object invoke(Object object, String methodName, Object... args)
  throws ClassCastException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
  {
    Class<?>[] argsClass;
    Method method;

    argsClass = new Class[args.length];
    for (int i = 0; i < args.length; i++)
    {
      argsClass[i] = args[i] == null ? Object.class : args[i].getClass();
    }
    method = ReflectUtil.getMethod(object.getClass(), methodName, argsClass);
    return MethodReflect.invoke(object, method, args);
  }

  /**
   * 通过已有的方法对象执行反射操作，如果不能找到完全匹配的方法会尝试自动转换类型查找最匹配的方法，自动转换失败会抛出
   * {@link ClassCastException} 异常。
   * <p/>
   * @param object
   * 需要执行反射操作的对象。
   * @param method
   * 需要调用的方法对象，它是一个 {@link Method} 对象。
   * @param args
   * 调用方法时附带的参数，它可以是一个数组。
   * <p/>
   * @return 方法执行所返回的结果。
   * <p/>
   * @th java.lang.ClassCastException
   * 如果是自动匹配的方法在无法自动转换参数时会引发此异常。
   * @th java.lang.IllegalAccessException
   * 由 {@link Method#invoke(Object, Object...)} 方法引发。
   * @th java.lang.IllegalArgumentException
   * 由 {@link Method#invoke(Object, Object...)} 方法引发。
   * @th java.lang.reflect.InvocationTargetException
   * 由 {@link Method#invoke(Object, Object...)} 方法引发。
   */
  public static Object invoke(Object object, Method method, Object... args)
  throws ClassCastException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
  {
    Class<?>[] methodArgs = method.getParameterTypes();

    for (int i = 0; i < args.length; i++)
    {
      args[i] = TypeUtils.castToType(args[i], methodArgs[i]);
    }

    return method.invoke(object, args);
  }

  /**
   * 通过完整方法名称及参数查找方法，支持方法重载。
   */
  public static Method findMethod(Class<?> c, String name, boolean caseSensitive, Class<?>... classes)
  {
    Method m;
    Method[] ms;
    Class<?>[] methodClasses;

    try
    {
      m = c.getMethod(name, classes);

      if (m != null)
      {
        return m;
      }
    }
    catch(SecurityException e)
    {
      ;
    }
    catch(NoSuchMethodException e)
    {
      ;
    }

    ms = c.getMethods();

    for (int i = 0; i < ms.length; i++)
    {
      if ((caseSensitive && ms[i].getName().equals(name))
          || (!caseSensitive && ms[i].getName().equalsIgnoreCase(name)))
      {
        methodClasses = ms[i].getParameterTypes();

        /*
         * 过滤重载方法
         */
        if (methodClasses.length == classes.length)
        {

          /*
           * 检查实参类是否可以转换为形参类
           */
          for (int j = 0; j < methodClasses.length; j++)
          {
            if (!TypeUtils.testCastToType(classes[j], methodClasses[j]))
            {
              break;
            }
          }

          return ms[i];
        }
      }
    }

    return null;
  }

  /**
   * 通过完整方法名称及参数查找方法。
   */
  public static Method findMethod(Class<?> c, String name, Class<?>... classes)
  {
    return findMethod(c, name, true, classes);
  }

}
